﻿//  --------------------------------
//  Copyright (c) AW Systems, Inc. All rights reserved.
//  This source code is made available under the terms of the Microsoft Public License (Ms-PL)
//  http://www.codeplex.com/SLAM/license
//  ---------------------------------

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data.Linq;
using System.Linq;
using Slam.Administration.Data;
using Slam.Configuration;
using Slam.SharePoint;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;

namespace Slam.Administration
{
	public partial class Configuration : LayoutsPageBase
	{   
		private string slamFolder = "";

		protected void Page_Load(object sender, EventArgs e)
		{
			//if under a site collection other than the root, doing a simple
			//redirect to the page will always redirect to the root, so the desired
			//location needs extracted from the RawUrl
			slamFolder = Request.RawUrl.Substring(0, Request.RawUrl.LastIndexOf("/") + 1);

			if (!IsPostBack)
			{
				//Test if SLAM can clear instances, if it cannot go back to the Control Panel
				try
				{
					ComponentService.ClearInstances();
				}
				catch
				{
					Response.Redirect(slamFolder + "SLAMControlPanel.aspx");
				}

				validationResults.Visible = false;

				if (Request.QueryString["NewSlamIdSeed"] != null)
				{
					int slamIdSeed = 0;
					if (Int32.TryParse(Request.QueryString["NewSlamIdSeed"], out slamIdSeed))
					{
						SPSite currentSiteCollection = SPControl.GetContextSite(Context);
						SPWeb rootWeb = currentSiteCollection.RootWeb;
						rootWeb.Properties["SlamIdNumber"] = slamIdSeed.ToString();
						rootWeb.AllowUnsafeUpdates = true;
						rootWeb.Properties.Update();
					}
				}
			}
			else
			{
				ComponentService.ClearInstances();
			}
		}


		/// <summary>
		/// Event handler for validating the SLAM configuration
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected void btnValidateConfiguration_Click(object sender, EventArgs e)
		{
			btnRefresh.Visible = true;
			validationResults.Visible = true;
			optionsPanel.Visible = false;
			
			lblErrorMessages.Text = "";

			//Track all matched types, all unmatched types
			List<string> unmatchedTypes = new List<string>();
			List<string> matchedTypes = new List<string>();

			//Track all matched fields for all matched types 
			//and all unmatched fields for matched types
			List<string> matchedFields = new List<string>();
			List<string> unmatchedFields = new List<string>();

			try
			{
				SPWeb site = SPContext.Current.Web;
				SPSecurity.RunWithElevatedPrivileges(delegate()
				{
					using (SPSite elevatedSite = new SPSite(site.Site.ID))
					{
						SPWeb elevatedWeb = null;
						//Get SLAM configuration
						IConfigurationManager configuration = ComponentService.GetInstance<IConfigurationManager>();
						//Open ConnectionStrings section
						ConnectionStringSection connections = configuration.GetSection<ConnectionStringSection>("ConnectionStrings");

						DataContext dataContext = new DataContext(connections.Default);
						//Check if connection is established, if it cannot be show error
						try
						{
							bool messageTableExists = dataContext.ExecuteQuery<int>(Queries.VerifyMessageLogExists).FirstOrDefault() > 0;
						}
						catch (Exception ex)
						{
							lblErrorMessages.Text = "SLAM is attempting to connect to the database using the Default connection string defined in the SLAM configuration file but is failing to connect.  It is receiving the following error:<br /><br />" + ex.Message + "<br /><br />";
						}

						//Determine what DataMapping to use from the configuration
						string siteCollection = elevatedSite.ServerRelativeUrl.TrimStart('/');
						DataMapping currentDataMapping = configuration.DataMapping;
						if (!String.IsNullOrEmpty(siteCollection) && configuration.DataMappings.ContainsKey(siteCollection))
						{
							currentDataMapping = configuration.DataMappings[siteCollection];
						}

						foreach (ConfiguredType type in currentDataMapping.GetAllTypes())
						{
							//For SqlConfiguredTypes the type name to use for getting type
							//information from the configuration should be the Table name if present
							//otherwise it should be the regular type Name.
							string configuredTypeName = type.Name;
							if (type is SqlConfiguredType && !String.IsNullOrEmpty(((SqlConfiguredType)type).Table))
							{
								configuredTypeName = ((SqlConfiguredType)type).Table;
							}

							//For tracking, store a qualified version of the type name in these formats:
							//Subsite List with ID: Site - ID - Name 
							//Root List with ID: ID - Name
							//Root List without ID or Root Content Type: Name
							//Subsite Content Type: Site - Name
							string typeString = String.Format("{0} - {1} - {2}", type.Site, type.Id, type.Name);

							//If Site is empty, open the Root Web
							if (String.IsNullOrEmpty(type.Site))
							{
								elevatedWeb = elevatedSite.RootWeb;

								if (!String.IsNullOrEmpty(type.Id))
								{
									typeString = String.Format("{0} - {1}", type.Id, type.Name);
								}
								else
								{
									typeString = type.Name;
								}
							}
							else
							{
								if (String.IsNullOrEmpty(type.Id))
								{
									typeString = String.Format("{0} - {1}", type.Site, type.Name);
								}

								try
								{
									elevatedWeb = elevatedSite.AllWebs[type.Site];

									//If Web does not exist, track type as unmatched
									if (!elevatedWeb.Exists)
									{
										unmatchedTypes.Add(typeString + " (Could not open Site)");
									}
								}
								catch
								{
									//If opening the Web for type.Site throws an exception, 
									//track type as unmatched
									unmatchedTypes.Add(typeString + " (Could not open Site)");
								}
							}

							if (elevatedWeb != null && elevatedWeb.Exists)
							{
								if (type.ContentType)
								{
									try
									{
										SPContentType contentType = null;
										try
										{
											contentType = elevatedWeb.ContentTypes[type.Name];
										}
										catch 
										{ 
											//If it fails to here, the contentType is obviously
											//not matched and will be tracked as such below
										}
										if (contentType != null)
										{
											SPListItem listItem = null;

											//To determine which configured fields match
											//we need an SPListItem.  To get one even
											//if all lists are empty, find a list that contains
											//this content type and add a new SPListItem to it
											//that we will not perform an Update against
											for (int i = 0; i < elevatedSite.AllWebs.Count; i++)
											{
												SPWeb subWeb = elevatedSite.AllWebs[i];
												SPContentTypeCollection listContentTypes = null;
												foreach (SPList list in subWeb.Lists)
												{
													listContentTypes = null;
													try
													{
														listContentTypes = list.ContentTypes;
													}
													catch (Exception ex)
													{
														//lblErrorMessages.Text += "<br />Type: " + typeString + "<br />List: " + list.Title + "<br />Exception: " + ex.ToString();
													}

													if (listContentTypes != null && listContentTypes.Contains(contentType))
													{
														//If content type is found, track it as matched
														if (!matchedTypes.Contains(typeString))
														{
															matchedTypes.Add(typeString);
														}

														listItem = list.Items.Add();
														listItem["ContentTypeId"] = contentType.Id;

														if (listItem != null)
														{
															List<Field> typeFields = currentDataMapping.GetTypeFields("", configuredTypeName).Where(f => !f.Name.Equals("ListID")).ToList();
															List<AssociationField> associationFields = currentDataMapping.GetTypeAssociations("", configuredTypeName).ToList();
															PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(listItem);

															//Iterate through each configured field for the content type
															foreach (Field currentField in typeFields)
															{
																//Because SLAM can handle properties, check if the currentField is a property of the listItem
																PropertyDescriptor property = properties.Find(currentField.Name, false);

																//If the currentField is a matched property of a matched listItem Field, track it as 
																//matched, otherwise track it as unmatched
																if (property != null || listItem.Fields.ContainsField(currentField.Name))
																{
																	if (!matchedFields.Contains(String.Format("{0}: {1}", typeString, currentField.Name)))
																	{
																		matchedFields.Add(String.Format("{0}: {1}", typeString, currentField.Name));
																	}
																}
																else
																{
																	if (!unmatchedFields.Contains(String.Format("{0}: {1}", typeString, currentField.Name)))
																	{
																		unmatchedFields.Add(String.Format("{0}: {1}", typeString, currentField.Name));
																	}
																}
															}

															//Iterate through each configured association field for the content type
															foreach (AssociationField currentField in associationFields)
															{
																//If the currentField is a matched listItem Field, track it as 
																//matched, otherwise track it as unmatched
																if (listItem.Fields.ContainsField(currentField.Name))
																{
																	if (!matchedFields.Contains(String.Format("{0}: {1}", typeString, currentField.Name)))
																	{
																		matchedFields.Add(String.Format("{0}: {1}", typeString, currentField.Name));
																	}
																}
																else
																{
																	if (!unmatchedFields.Contains(String.Format("{0}: {1}", typeString, currentField.Name)))
																	{
																		unmatchedFields.Add(String.Format("{0}: {1}", typeString, currentField.Name));
																	}
																}
															}
														}
													}
												}
												subWeb.Dispose();
											}
											if (!matchedTypes.Contains(typeString))
											{
												//If the content type wasn't found, track it as unmatched
												unmatchedTypes.Add(typeString);
											}
										}   
									}
									catch (Exception ex)
									{
										//If processing the content type threw an exception, track it as unmatched
										//then show the error
										unmatchedTypes.Add(typeString);
										lblErrorMessages.Text += "Type: " + typeString + "<br />Exception: " + ex.ToString();
									}
								}
								else //If not a content type
								{
									string extraMatchedMessage = "";

									//Check for a valid configured ID if present
									Guid guidId = Guid.Empty;
									if (!String.IsNullOrEmpty(type.Id))
									{
										try
										{
											guidId = new Guid(type.Id);
										}
										catch (Exception ex)
										{
											//If ID is not a valid GUID, show an error and if the type 
											//continues to match show the qualification that despite the 
											//ID's presence in the configuration the matching is happening
											//on the type's Name
											lblErrorMessages.Text += String.Format("List ID not a valid Guid.<br />Type: {0}<br />ID: {1}<br /><br />", type.Name, type.Id);
											extraMatchedMessage = " (Matched by Name)";
										}
									}

									SPList list = null;
									try
									{
										try
										{
											if (guidId != Guid.Empty)
											{
												list = elevatedWeb.Lists[guidId];
											}
											else
											{
												list = elevatedWeb.Lists[type.Name];
											}
										}
										catch 
										{ 
											//If it fails to here, the list cannot be found which
											//will be tracked below
										}

										if (list != null)
										{
											//If the list is found, track it as matched
											matchedTypes.Add(typeString + extraMatchedMessage);

											//To check if configured fields match an SPListItem
											//must exist in the list.  To easily perform the validation
											//even on an empty list add a new SPListItem that we will
											//not Update
											SPListItem listItem = list.Items.Add();

											if (listItem != null)
											{
												List<Field> typeFields = currentDataMapping.GetTypeFields(type.Id, configuredTypeName).Where(f => !f.Name.Equals("ListID")).ToList();
												List<AssociationField> associationFields = currentDataMapping.GetTypeAssociations(type.Id, configuredTypeName).ToList();
												PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(listItem);

												//Iterate through each configured field for the list
												foreach (Field currentField in typeFields)
												{
													//Because SLAM can handle properties, check if the currentField is a property of the listItem
													PropertyDescriptor property = properties.Find(currentField.Name, false);

													//If the currentField is a matched property of a matched listItem Field, track it as 
													//matched, otherwise track it as unmatched
													if (property != null || listItem.Fields.ContainsField(currentField.Name))
													{
														if (!matchedFields.Contains(String.Format("{0}: {1}", typeString, currentField.Name)))
														{
															matchedFields.Add(String.Format("{0}: {1}", typeString, currentField.Name));
														}
													}
													else
													{
														if (!unmatchedFields.Contains(String.Format("{0}: {1}", typeString, currentField.Name)))
														{
															unmatchedFields.Add(String.Format("{0}: {1}", typeString, currentField.Name));
														}
													}
												}

												//Iterate through each configured association field for the list
												foreach (AssociationField currentField in associationFields)
												{
													//If the currentField is a matched listItem Field, track it as 
													//matched, otherwise track it as unmatched
													if (listItem.Fields.ContainsField(currentField.Name))
													{
														if (!matchedFields.Contains(String.Format("{0}: {1}", typeString, currentField.Name)))
														{
															matchedFields.Add(String.Format("{0}: {1}", typeString, currentField.Name));
														}
													}
													else
													{
														if (!unmatchedFields.Contains(String.Format("{0}: {1}", typeString, currentField.Name)))
														{
															unmatchedFields.Add(String.Format("{0}: {1}", typeString, currentField.Name));
														}
													}
												}
											}
										}
										else
										{
											//If the list wasn't found, track it as unmatched
											unmatchedTypes.Add(typeString);
										}
									}
									catch (Exception ex)
									{
										//If processing the content type threw an exception, track it as unmatched
										//then show the error
										unmatchedTypes.Add(typeString);
										lblErrorMessages.Text += "Type: " + typeString + "<br />Exception: " + ex.ToString();
									}
								}
							}

							if (elevatedWeb != null)
							{
								elevatedWeb.Dispose();
							}
						}
					}
				});


				//If types were matched, show the results
				rptMatchedTypes.Visible = matchedTypes.Count > 0;
				rptMatchedTypes.DataSource = matchedTypes;
				rptMatchedTypes.DataBind();

				//If types were not matched, show the results
				rptUnmatchedTypes.Visible = unmatchedTypes.Count > 0;
				rptUnmatchedTypes.DataSource = unmatchedTypes;
				rptUnmatchedTypes.DataBind();

				//If fields were matched, show the results
				rptMatchedFields.Visible = matchedFields.Count > 0;
				rptMatchedFields.DataSource = matchedFields;
				rptMatchedFields.DataBind();

				//If fields were not matched, show the results
				rptUnmatchedFields.Visible = unmatchedFields.Count > 0;
				rptUnmatchedFields.DataSource = unmatchedFields;
				rptUnmatchedFields.DataBind();
			}
			catch (Exception ex)
			{
				//In case anything else threw an error, show it
				lblErrorMessages.Text = ex.ToString();
			}
		}

		protected void btnBack_Click(object sender, EventArgs e)
		{   
			Response.Redirect(slamFolder + "SLAMControlPanel.aspx");
		}
	}
}
